use std::{
    cell::RefCell,
    collections::{hash_map::Entry, HashMap},
    rc::Rc,
};

use crate::Syntax::syntaxResult::SyntaxResult;

use super::{
    object::{self, *},
    token::SyntaxToken,
};

#[derive(Debug)]
pub struct Environment {
    values: HashMap<String, Object>,
    enclosing: Option<Rc<RefCell<Environment>>>,
}

impl Environment {
    pub fn new() -> Self {
        Self {
            values: HashMap::new(),
            enclosing: None,
        }
    }
    pub fn newWithEnclosing(_enclosing: Rc<RefCell<Environment>>) -> Self {
        Self {
            values: HashMap::new(),
            enclosing: Some(_enclosing),
        }
    }
    pub fn define(&mut self, _name: &String, _obj: Object) {
        self.values.insert(_name.to_string(), _obj);
    }
    pub fn get(&self, _token: &SyntaxToken) -> Result<Object, SyntaxResult> {
        if let Some(value) = self.values.get(_token.as_string()) {
            return Ok(value.clone());
        } else if let Some(_enclosing) = &self.enclosing {
            return _enclosing.borrow().get(_token);
        }
        // let runtimeErr = SyntaxResult::RuntimeError(
        //     token,
        //     &format!("Undefined variable '{}'. ", token.as_string()),
        // );

        //eprintln!("Undefined variable '{}'. ", token.as_string());
        Err(SyntaxResult::RuntimeError(_token, "Undefined variable "))
    }
    pub fn assign(&mut self, _token: &SyntaxToken, _obj: Object) -> Result<(), SyntaxResult> {
        if let Entry::Occupied(mut object) = self.values.entry(_token.as_string().to_string()) {
            object.insert(_obj);
            return Ok(());
        } else if let Some(_enclosing) = &self.enclosing {
            return _enclosing.borrow_mut().assign(_token, _obj);
        }
        Err(SyntaxResult::RuntimeError(
            _token,
            &format!("Undefined variable'{}'.", _token.as_string()),
        ))
    }
}
